#pragma once

#include <cstdint>
#include <cstdlib>
#include <cstdio>
#include <ctime>
#include <unordered_map>
#include <unordered_set>
#include <list>
#include <vector>
#include <queue>
#include <memory>
#include <atomic>
#include <iostream>
#include <sstream>
#include <thread>
#include <mutex>
#include <string>

#define USE_PLAIN_ALLOCATOR 0

constexpr int ModuleFrame_timerMap_ = 1;
constexpr int ModuleFrame_locatorHandlerMap_ = 2;
constexpr int ModuleFrame_typePathMap_ = 3;
constexpr int AoiScene_entityMap_ = 4;
constexpr int ClientManager_clientMap_ = 5;
constexpr int Client_pathMap_ = 6;
constexpr int EntitySearchCacheManager_searchCallMap_ = 7;
constexpr int EntitySearchCacheManager_cacheMap_ = 8;
constexpr int EntitySearchCacheManager_pendingSearches_ = 9;
constexpr int Framework_registeredEntityMap_ = 10;
constexpr int Framework_pendingEntityMap_ = 11;
constexpr int EngineImpl_prefindMap_ = 12;
constexpr int EngineImpl_locatorEntityMap_ = 13;
constexpr int EngineImpl_locatorEntityTypeMap_ = 14;
constexpr int PathPool_sitePathMap_ = 15;
constexpr int PathPool_siteIDPathMap_ = 16;
constexpr int ActorLocator_actorMap_ = 17;
constexpr int ActorLocator_typeMap_ = 18;
constexpr int ActorLocator_timerActorMap_ = 19;
constexpr int ActorTimerManager_timerHandlers_ = 20;
constexpr int EventQueue_eventHandlerMap_ = 21;
constexpr int EventQueue_timerEventMap_ = 22;
constexpr int EventQueue_listenerMap_ = 23;
constexpr int Grid_sites_ = 24;
constexpr int Grid_acceptedSessions_ = 25;
constexpr int Grid_sessions_ = 26;
constexpr int PathManager_pathMap_ = 27;
constexpr int TcpIoLoop_chanMap_ = 28;
constexpr int Selector_channels_ = 29;
constexpr int Selector_sessions_ = 30;
constexpr int EventBasedObjectLocator_objectMap_ = 31;
constexpr int EntitySearchCache_entityIDSet_ = 32;
constexpr int EntitySearchCacheManager_searchTypeSet_ = 33;
constexpr int ActorLocator_TypeMap_value = 34;
constexpr int TimerQueue_timerIDSet_ = 35;

#define TYPE_CASE(name)\
    case name:\
        return #name;

static inline const char* getType(int type) {
    switch (type) {
        TYPE_CASE(ModuleFrame_timerMap_)
        TYPE_CASE(ModuleFrame_locatorHandlerMap_)
        TYPE_CASE(ModuleFrame_typePathMap_)
        TYPE_CASE(AoiScene_entityMap_)
        TYPE_CASE(ClientManager_clientMap_)
        TYPE_CASE(Client_pathMap_)
        TYPE_CASE(EntitySearchCacheManager_searchCallMap_)
        TYPE_CASE(EntitySearchCacheManager_cacheMap_)
        TYPE_CASE(EntitySearchCacheManager_pendingSearches_)
        TYPE_CASE(Framework_registeredEntityMap_)
        TYPE_CASE(Framework_pendingEntityMap_)
        TYPE_CASE(EngineImpl_prefindMap_)
        TYPE_CASE(EngineImpl_locatorEntityMap_)
        TYPE_CASE(EngineImpl_locatorEntityTypeMap_)
        TYPE_CASE(PathPool_sitePathMap_)
        TYPE_CASE(PathPool_siteIDPathMap_)
        TYPE_CASE(ActorLocator_actorMap_)
        TYPE_CASE(ActorLocator_typeMap_)
        TYPE_CASE(ActorLocator_timerActorMap_)
        TYPE_CASE(ActorTimerManager_timerHandlers_)
        TYPE_CASE(EventQueue_eventHandlerMap_)
        TYPE_CASE(EventQueue_timerEventMap_)
        TYPE_CASE(EventQueue_listenerMap_)
        TYPE_CASE(Grid_sites_)
        TYPE_CASE(Grid_acceptedSessions_)
        TYPE_CASE(Grid_sessions_)
        TYPE_CASE(PathManager_pathMap_)
        TYPE_CASE(TcpIoLoop_chanMap_)
        TYPE_CASE(Selector_channels_)
        TYPE_CASE(Selector_sessions_)
        TYPE_CASE(EventBasedObjectLocator_objectMap_)
        TYPE_CASE(EntitySearchCache_entityIDSet_)
        TYPE_CASE(EntitySearchCacheManager_searchTypeSet_)
        TYPE_CASE(ActorLocator_TypeMap_value)
        TYPE_CASE(TimerQueue_timerIDSet_)
    }
    return "";
}

namespace kratos { namespace corelib {

#define AtomCoutEx AtomicWrite()

template <typename T, const int Type = 0>
class TestingAllocator : public std::allocator<T>
{
public:
    typedef std::size_t size_type;
    typedef T* pointer;
    typedef const T* const_pointer;
    typedef T& reference;
    typedef const T& const_reference;
    typedef T value_type;

    std::size_t size_;
    std::size_t gap_;
    int type_;

    template <typename U>
    struct rebind {
        typedef TestingAllocator<U, Type> other;
    };
    TestingAllocator() {
        size_ = 0;
        type_ = Type;
    }
    TestingAllocator(const TestingAllocator<T, Type>& rht) {
        size_ = rht.size_;
        type_ = rht.type_;
    }
    template <typename U>
    TestingAllocator(const TestingAllocator<U, Type>& rht) {
        size_ = rht.size_;
        type_ = rht.type_;
    }
    ~TestingAllocator() {
    }
    pointer allocate(size_type n, const void*) {
        return allocate(n);
    }
    class AtomicWrite : public std::ostream
    {
    public:
        AtomicWrite(): std::ostream(0)
            , _os()
        {
            this->init(_os.rdbuf());
        }
        ~AtomicWrite(){
            std::cout << _os.str();
        }
    private:
        std::ostringstream _os;
    };
    pointer allocate(size_type n) {
        size_ += 1;
        gap_ += 1;
        if (gap_ > 512) {
            gap_ = 0;
            if (type_) {
                if (size_ > 16) {
                    printf("%s, Size: %d\n", getType(type_), size_);
                }
            }
        }
        return (pointer)malloc(n * sizeof(T));
    }
    void deallocate(pointer p, size_type) {
        size_ -= 1;
        free(p);
    }
};

#if USE_PLAIN_ALLOCATOR

template <typename T, typename Compare>
using kratos_priority_queue = std::priority_queue<T, std::vector<T>, Compare>;

#define UNORDERED_MAP(K, V, NAME)\
    std::unordered_map<K, V, std::hash<K>, std::equal_to<K>, kratos::corelib::TestingAllocator<std::pair<K, V>, NAME>>

#define UNORDERED_SET(K, NAME)\
    std::unordered_set<K, std::hash<K>, std::equal_to<K>, kratos::corelib::TestingAllocator<K, NAME>>

#else

template <typename T, typename Compare>
using kratos_priority_queue = std::priority_queue<T, std::vector<T>, Compare>;

#define UNORDERED_MAP(K, V, NAME)\
    std::unordered_map<K, V, std::hash<K>, std::equal_to<K>>

#define UNORDERED_SET(K, NAME)\
    std::unordered_set<K, std::hash<K>, std::equal_to<K>>

#endif

}}
